﻿#include "CollisionCalculate.h"
#include "GeometricCalculation.h"

#include "GnsPolygon2.h"		// 二维多边形

USING_NS_CC;
//using namespace CocosDenshion;
//USING_NS_CC_EXT;   //  看定义就知道等效于  using namespace cocos2d::extension

#define USE_THREAD_CAL  1  // 1 表示用线程计算碰撞，0表示在 update 里计算碰撞

CollisionCalculate::CollisionCalculate()
{
	CCLOG("CollisionCalculate  constructor ");

	// 随机数的种子
	srand((unsigned)time(nullptr));
}

CollisionCalculate::~CollisionCalculate()
{
	CCLOG("CollisionCalculate  destructor ");
}

// 另一种判断多边形顶点排列方向的方法      
bool CollisionCalculate::isccwize(int vcount, Vec2 polygon[])
{
	int i, index;
	Vec2 a, b, v;
	v = polygon[0];
	index = 0;
	for (i = 1; i < vcount; i++) // 找到最低且最左顶点，肯定是凸顶点     
	{
		if (polygon[i].y < v.y || polygon[i].y == v.y && polygon[i].x<v.x)
		{
			index = i;
		}
	}
	a = polygon[(index - 1 + vcount) % vcount]; // 顶点v的前一顶点     
	b = polygon[(index + 1) % vcount]; // 顶点v的后一顶点     
	return multiply(v, b, a)>0;
}

// 点与简单多边形的碰撞检测，顶点逆时针排列，射线法判断点q与多边形polygon的位置关系，要求polygon为简单多边形
int CollisionCalculate::insidepolygon(int vcount, Vec2 Polygon[], Vec2 q)
{
	int c = 0, i, n;
	LINESEG l1, l2;
	bool bintersect_a, bonline1, bonline2, bonline3;
	float r1, r2;

	l1.s = q;
	l1.e = q;
	l1.e.x = float(INF);
	n = vcount;
	for (i = 0; i<vcount; i++)
	{
		l2.s = Polygon[i];
		l2.e = Polygon[(i + 1) % n];
		if (online(l2, q))
			return 1; // 如果点在边上，返回1     
		if ((bintersect_a = intersect_A(l1, l2)) || // 相交且不在端点     
			((bonline1 = online(l1, Polygon[(i + 1) % n])) && // 第二个端点在射线上     
			((!(bonline2 = online(l1, Polygon[(i + 2) % n]))) && /* 前一个端点和后一个端点在射线两侧 */
			((r1 = multiply(Polygon[i], Polygon[(i + 1) % n], l1.s)*multiply(Polygon[(i + 1) % n], Polygon[(i + 2) % n], l1.s))>0) ||
			(bonline3 = online(l1, Polygon[(i + 2) % n])) &&     /* 下一条边是水平线，前一个端点和后一个端点在射线两侧  */
			((r2 = multiply(Polygon[i], Polygon[(i + 2) % n], l1.s)*multiply(Polygon[(i + 2) % n],
			Polygon[(i + 3) % n], l1.s))>0)
			)
			)
			) c++;
	}
	if (c % 2 == 1)
		return 0;
	else
		return 2;
}


// 点与凸多边形的碰撞检测，注意：多边形 polygon 一定要是凸多边形
bool CollisionCalculate::InsideConvexPolygon(int vcount, Vec2 polygon[], Vec2 q) // 可用于三角形！     
{
	Vec2 p;
	LINESEG l;
	int i;
	p.x = 0; p.y = 0;
	for (i = 0; i<vcount; i++) // 寻找一个肯定在多边形polygon内的点p：多边形顶点平均值     
	{
		p.x += polygon[i].x;
		p.y += polygon[i].y;
	}
	p.x /= vcount;
	p.y /= vcount;

	for (i = 0; i<vcount; i++)
	{
		l.s = polygon[i]; l.e = polygon[(i + 1) % vcount];
		if (multiply(p, l.e, l.s)*multiply(q, l.e, l.s)<0) /* 点p和点q在边l的两侧，说明点q肯定在多边形外 */
			break;
	}
	return (i == vcount);
}

// 点与多边形的碰撞检测，推荐使用，注意：多边形可以不是简单多边形，可以不是凸多边形 
bool CollisionCalculate::InsidePolygon(int n, Vec2 polygon[], Vec2 point)
{
	if (n == 1)
	{
		return   ((fabs(polygon[0].x - point.x)<EP) && (fabs(polygon[0].y - point.y)<EP));
	}
	else if (n == 2)
	{
		LINESEG side;
		side.s = polygon[0];
		side.e = polygon[1];
		return   online(side, point);
	}

	int   count = 0;
	LINESEG line;
	line.s = point;
	line.e.y = point.y;
	line.e.x = -INF;
	for (int i = 0; i<n; i++)
	{
		//   得到多边形的一条边       
		LINESEG   side;
		side.s = polygon[i];
		side.e = polygon[(i + 1) % n];

		if (online(side, point))   {
			return   true;
		}

		//如果side平行x轴则不作考虑       
		if (fabs(side.s.y - side.e.y)<EP)
		{
			continue;
		}

		if (online(line, side.s))   {
			if (side.s.y   >   side.e.y)   count++;
		}
		else   if (online(line, side.e))   {
			if (side.e.y   >   side.s.y)   count++;
		}
		else   if (intersect(line, side))   {
			count++;
		}
	}

	return (count % 2 == 1);
}

// 点与圆的碰撞检测，点p在圆内(包括边界)时
bool CollisionCalculate::point_in_circle(Vec2 o, float r, Vec2 p)
{
	float d2 = (p.x - o.x)*(p.x - o.x) + (p.y - o.y)*(p.y - o.y);
	float r2 = r*r;
	return d2 < r2 || fabs(d2 - r2) < EP;
}

// 圆与多边形的碰撞检测，多边形的点在圆内(包括边界)时
bool CollisionCalculate::CircleInsidePolygon(int vcount, Vec2 center, float radius, Vec2 polygon[])
{
	Vec2 q;
	float d;
	q.x = 0;
	q.y = 0;
	d = ptopointset(vcount, polygon, center, q);
	if (d < radius || fabs(d - radius) < EP)
		return true;
	else
		return false;
}

// 返回 线段 L1 与 线段L2之间的角度，360度那种角度
float CollisionCalculate::getAngleInTwoLine(Vec2 s1, Vec2 e1, Vec2 s2, Vec2 e2)
{
	LINESEG l1;		// 线段1
	LINESEG l2;		// 线段2

	l1.s = s1;
	l1.e = e1;
	l2.s = s2;
	l2.e = e2;

	Vec2 o, s, e;
	o.x = o.y = 0;
	s.x = l1.e.x - l1.s.x;
	s.y = l1.e.y - l1.s.y;
	e.x = l2.e.x - l2.s.x;
	e.y = l2.e.y - l2.s.y;

	float radian = angle(o, s, e);	// 弧度值
	float angle = Mat44::radianToAngle(radian);   // 角度值

	// 顺时针方向旋转为正数，逆时针方向旋转为负数，cocos2d的坐标系与OpenGL的坐标系旋方向是相反的
	angle = -angle;

	return angle;
}
